【Greengrass V2】カスタムコンポーネントでRaspberry Pi から Modbus プロトコルで温湿度データを取得してみた
前回の Modbus 温湿度センサー を使って、Greengrass V2 でセンサーデータを取得してみたいと思います。今回は Greengrass Core デバイスとして Raspbessy Pi 4 を使います。
前回の記事はこちらです。
前回と同じように Modbus センサーと Raspberry Pi を写真のように接続します。
Python環境の整備
今回も Python を使いますが、Raspbessy Pi の Python 環境は下記のとおりでした。特に問題ないので、このまま利用します。
$ python -V Python 2.7.16 $ python3 -V Python 3.7.3
コード自体は前回のものとほぼ同じものを使います。そのため事前にpymodbus
だけインストールしておきましょう
$ pip3 install pymodbus
Greengrass を使わずに温湿度データを取得する
Greengrass を試す前に前回のコードをベースに、Greengrass を使わずに単純に Raspberry Pi 上で温湿度が取得できることを確認してみます。
コードは下記のとおりです。適当な場所に下記ファイルを作成してください。
なお、今回は温湿度データを10秒間隔で取得するように前回のコードを少し修正しました。また、ModbusClient()
のport
指定も Raspberry Pi に合わせて /dev/ttyUSB0
に変更しました。
$ vi ~/test-modbus-sensor.py
from pymodbus.client.sync import ModbusSerialClient as ModbusClient import datetime import time def run_sync_client(): client = ModbusClient(baudrate=9600, port="/dev/ttyUSB0", method="rtu") client.connect() rr = client.read_input_registers(address=1, count=2, unit=0x1) temperature = rr.registers[0]/10 humidity = rr.registers[1]/10 # Append the message to the log file. with open('/tmp/Greengrass_modbus_sensor2.log', 'a') as f: print(f"{str(datetime.datetime.now())}\tTemperatur:\t{temperature} ℃", file=f) print(f"{str(datetime.datetime.now())}\tHumidity:\t{humidity} %", file=f) client.close() while True: if __name__ == "__main__": run_sync_client() time.sleep(10)
実行してみましょう
$ python3 ~/test-modbus-sensor.py
スクリプトを実行しているターミナルとは別のものを開いて確認します。下記のように温湿度が記録されていればOKです。
$ tail -f /tmp/Greengrass_modbus_sensor.log 2021-08-12 06:49:03.881930 Temperatur: 28.3 ℃ 2021-08-12 06:49:03.881979 Humidity: 56.2 % 2021-08-12 06:50:03.576678 Temperatur: 28.3 ℃ 2021-08-12 06:50:03.576738 Humidity: 56.4 % 2021-08-12 06:50:13.623309 Temperatur: 28.4 ℃ 2021-08-12 06:50:13.623406 Humidity: 56.4 %
お気づきの方もいらっしゃると思いますが、Raspberry Pi なので GPIO 経由でデータ取得することも可能です。しかし、GPIO で取得するには追加でデバイスが必要になるため、今回は前回と同様にシリアルポート経由でデータ取得することにしました。
Greengrass V2のセットアップ
スクリプトの動作確認ができたので次は Greengrass のコンポーネントとして動かしてみたいと思います。
今回は Raspberry Pi 4 (4GB) を Greengrass Core デバイスとしてセットアップを行います。セットアップの手順は下記のブログが参考になると思いますので、本記事では手順を割愛させていただきます。(気になる点もあるので、改めて記事にしたいと思います)
Greengrass Core ソフトウェアのインストール時に、Greengrass CLI も同時にインストールしているものします。
なお、作業は基本的に全てpi ユーザー
で行います。
レシピの作成
まず最初にコンポーネント作成用のディレクトリを用意しましょう。(適当なディレクトリを作ってください)
$ mkdir ~/MyComponent
このディレクトリを起点に下記のような構成としました。
. ├── artifacts │ └── com.example.ModbusSensor │ └── 1.0.0 │ └── modbus_sensor.py └── recipes └── com.example.ModbusSensor-1.0.0.yaml
次にレシピ用ディレクトリを作成します。
$ cd ~/MyComponent/ $ mkdir recipes
レシピ用のディレクトリができたらレシピを作成します。
$ vi recipes/com.example.ModbusSensor-1.0.0.yaml
レシピの内容は次のとおりです。
RecipeFormatVersion: '2020-01-25' ComponentName: com.example.ModbusSensor ComponentVersion: 1.0.0 ComponentDescription: Modbus sensor component. ComponentPublisher: Amazon Manifests: - Platform: os: linux Lifecycle: Run: | python3 -u {artifacts:path}/modbus_sensor.py
アーティファクトのソースコード
次に処理の実体となるアーティファクトのコードを用意します。はじめにアーティファクトのフォルダを作成します。
$ cd ~/MyComponent/ $ mkdir -p artifacts/com.example.ModbusSensor/1.0.0
次にアーティファクトのコードを用意します。
$ vi artifacts/com.example.ModbusSensor/1.0.0/modbus_sensor.py
コードは下記のとおりですが、先程単体で確認したものと全く同じです。
from pymodbus.client.sync import ModbusSerialClient as ModbusClient import datetime import time def run_sync_client(): client = ModbusClient(baudrate=9600, port="/dev/ttyUSB0", method="rtu") client.connect() rr = client.read_input_registers(address=1, count=2, unit=0x1) temperature = rr.registers[0]/10 humidity = rr.registers[1]/10 # Append the message to the log file. with open('/tmp/Greengrass_modbus_sensor.log', 'a') as f: print(f"{str(datetime.datetime.now())}\tTemperatur:\t{temperature} ℃", file=f) print(f"{str(datetime.datetime.now())}\tHumidity:\t{humidity} %", file=f) client.close() while True: if __name__ == "__main__": run_sync_client() time.sleep(10)
直面した問題
「後はコンポーネントをデプロイすればOK」と思っていましたが、デプロイしても「FAILED」となりうまく行きませんでした。
実際に/greengrass/v2/logs
以下のログ(com.example.ModbusSensor.log
)を見ると次のようなエラーが出ていました。
pymodbus.exceptions.ConnectionException: Modbus Error: [Connection] Failed to connect[ModbusSerialClient(rtu baud[9600])]
先程はpi
ユーザーで直接スクリプトを実行して正常動作を確認しました。試しにuseradd
で別のユーザーを作成して同じスクリプトを実行してみたところ、同様のエラーとなりました。
Greengrass のコンポーネントはデフォルトでggc_user
で実行されるので、このユーザでシリアルポートにアクセスできていないことが原因のようです。色々調べているうちに、Linux 系OS の場合、シリアルポートにアクセスできるのは dialout
グループのユーザーだけということが分かりました。(初めて知りました。)
確かに/etc/group
を見るとpi
ユーザーは dialout
グループに所属しています。
dialout:x:20:pi
原因が分かったのでggc_user
ユーザーをdialout
グループに追加します。
$ sudo gpasswd -a ggc_user dialout
追加できました。
$ grep dialout /etc/group dialout:x:20:pi,ggc_user
参考URL
コンポーネントのデプロイ
準備ができたのでデプロイしてみます。
sudo /greengrass/v2/bin/greengrass-cli deployment create \ --recipeDir ~/MyComponent/recipes \ --artifactDir ~/MyComponent/artifacts \ --merge "com.example.ModbusSensor=1.0.0"
確認します。Deployment Id
には上記のデプロイコマンドを実行して表示されたものを指定します。
sudo /greengrass/v2/bin/greengrass-cli deployment status -i <Deployment Id>
次のようにデプロイ結果がSUCCEEDED
になればOKです。IN_PROGRESS
と出るときは少し待ってから再度確認しましょう。(10秒程度)
<Deployment Id>: SUCCEEDED
デプロイできたらログを確認してみます。
$ tail -f /tmp/Greengrass_modbus_sensor.log
先程と同様の出力ですが、今回は Greengrass のコンポーネントとしてデータ取得・出力できていることが分かりました。
2021-08-12 07:34:34.202113 Temperatur: 28.4 ℃ 2021-08-12 07:34:34.202208 Humidity: 52.8 % 2021-08-12 07:34:44.248452 Temperatur: 28.4 ℃ 2021-08-12 07:34:44.248550 Humidity: 53.1 % 2021-08-12 07:34:54.428230 Temperatur: 28.4 ℃ 2021-08-12 07:34:54.428330 Humidity: 53.3 %
最後に
はじめは「コードも前回と同じだからすぐにできるだろう」と思っていました。しかしコンポーネントとして動かすとエラーになる原因が分かりませんでした。
最初はレシピで制御できるのかと思いましたが、レシピではRequiresPrivilege
でroot
権限で動かすかどうかくらいしか選択肢がなかったので、強引にレシピで下記のような指定を試していました。(これでもデプロイは成功してセンサーデータを取得できます。)
sudo -u pi python3 {artifacts:path}/modbus_sensor.py
この場合、visudo
でpi
ユーザーのパスワード確認をスルーさせる必要があり、セキュアではないため悩みましたが、最終的に適切な解決策が見つかってよかったです。
次は、Lambda コンポーネント として試してみて、最後に AWS マネージドなコンポーネントである「Modbus-RTU プロトコルアダプタ」まで試していきたいと思います。
以上です。